<template>
  <!-- 地图容器 -->
  <div id="cesiumContainer"></div>

  <!-- 组件容器 -->
  <div class="container">
    <!-- 最近两个站点 -->
    <div class="fromTo">
      <div>{{ data.fromName }}</div>
      <img style="width: 3em; color: white" src="/src/assets/jt.png" />
      <div>{{ data.toName }}</div>
    </div>
    <!-- 所有站点信息 -->
    <div class="carInfo">
      <img @click="changeView('follow')" ref="img" class="img" src="/src/assets/icon.png" />
      <div
        @click="toStation(index)"
        :style="{ color: index == data.index ? '#f7bd42' : '#fff' }"
        style=""
        class="itemStation"
        v-for="(item, index) in data.carInfo.stations"
        :key="index"
      >
        <span class="num">{{ index + 1 }}</span> {{ item.name }}
      </div>
    </div>
    <!-- 控制器 -->
    <div class="control">
      <div class="iconList">
        <el-icon @click="toAdjust('pause')" title="暂停" v-show="data.control.play">
          <VideoPause />
        </el-icon>
        <el-icon @click="toAdjust('play')" title="播放" v-show="!data.control.play">
          <VideoPlay />
        </el-icon>
        <el-icon
          :style="{ color: data.control.speed == 5 ? '#808080' : '#fff' }"
          @click="toAdjust('-')"
          title="X0.5"
        >
          <DArrowLeft />
        </el-icon>
        <el-icon
          :style="{ color: data.control.speed == 160 ? '#808080' : '#fff' }"
          @click="toAdjust('+')"
          title="X2"
        >
          <DArrowRight />
        </el-icon>
        <el-icon title="重播" @click="toAdjust('replay')">
          <Refresh />
        </el-icon>
        <el-button type="primary" @click="changeView('follow')">跟随视角</el-button>
        <el-button type="primary" @click="changeView('incar')">车内视角</el-button>
        <el-button type="primary" @click="changeView('free')">自由视角</el-button>
        <span class="speed"
          >{{ data.control.speed }} <span style="font-size: 0.75em">m/s</span>
        </span>
      </div>
      <el-progress
        style="margin-top: 0.75em"
        striped-flow
        :percentage="data.control.percentage"
        :stroke-width="12"
        :show-text="false"
      />
    </div>
  </div>
</template>
<script setup>
import * as Cesium from 'cesium'
import * as turf from '@turf/turf'
import Bubble from './tool/bubbleCar'
import { onMounted, reactive, ref } from 'vue'
import { getSiteTimes, getSampleData, spaceDistance } from './tool/trajectory'
import modifyMap from './tool/filterColor'
import loadData from './assets/load.json'
let viewer, bubble, pickLabel, timeObj
const data = reactive({
  carInfo: {},
  index: 0,
  fromName: '',
  toName: '',
  control: {
    play: false,
    speed: 20,
    percentage: 0
  }
})

/*公交路线、标签、站牌的初始化方法*/
let positions = [],
  lineGeoJson,
  isBegin = false
const img = ref('')
const initData = () => {
  // 添加公交线路实体
  let xyArr = []
  let xArr = loadData.xs.split(',')
  let yArr = loadData.ys.split(',')
  xArr.forEach((item, index) => {
    positions.push(Cesium.Cartesian3.fromDegrees(Number(item), Number(yArr[index]))) // 拿轨迹点的笛卡尔坐标
    xyArr.push([Number(item), Number(yArr[index])]) // 拿轨迹点的经纬度坐标
  })
  const line = viewer.entities.add({
    polyline: {
      positions,
      width: 10,
      material: Cesium.Color.WHITE.withAlpha(0.8),
      clampToGround: true
    }
  })
  lineGeoJson = turf.lineString(xyArr) // 轨迹线的geojson格式
  data.carInfo = loadData
  data.fromName = loadData.stations[0].name
  data.toName = loadData.stations[1].name
  // 添加站点模型和标签
  loadData.stations.forEach((item, index) => {
    const position = Cesium.Cartesian3.fromDegrees(
      ...item.xy_coords.split(';').map((a) => Number(a))
    )
    const positionLabel = Cesium.Cartesian3.fromDegrees(
      ...item.xy_coords.split(';').map((a) => Number(a)),
      24
    )
    /// 设置模型的朝向
    const orientation = Cesium.Transforms.headingPitchRollQuaternion(
      position,
      Cesium.HeadingPitchRoll.fromDegrees(90, 0, 0)
    )
    viewer.entities.add({
      position,
      orientation,
      model: {
        uri: '/src/assets/model.gltf',
        scale: 0.07,
        minimumPixelSize: 10 // 最小像素值
      }
    })
    viewer.entities.add({
      position: positionLabel,
      id: 'label' + index, // 添加id，以便于后续视角跳转等
      label: {
        text: item.name,
        font: '10px Helvetica',
        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
        fillColor: Cesium.Color.WHITE, //字体颜色
        backgroundColor: Cesium.Color.BLACK.withAlpha(0.5), //背景颜色
        showBackground: true //是否显示背景颜色
      }
    })
  })
  viewer.zoomTo(line)
}
const toAdjust = (type) => {
  if (data.control.speed != 5 && type == '-') {
    data.control.speed /= 2
    viewer.clockViewModel.multiplier /= 2
  } else if (data.control.speed != 160 && type == '+') {
    data.control.speed *= 2
    viewer.clockViewModel.multiplier *= 2
  } else if (type == 'play' || type == 'replay') {
    if (type == 'replay') {
      img.value.style.top = '0.3em'
      img.value.style.left = '0.2em'
      data.control.percentage = 0
      data.index = 0
      viewer.clock.currentTime = viewer.clock.startTime
    }
    if (!isBegin) {
      isBegin = true
      toBegin()
    }
    data.control.play = true
    viewer.clock.shouldAnimate = true
  } else if (type == 'pause') {
    data.control.play = false
    viewer.clock.shouldAnimate = false
  }
}
/* 轨迹回放的核心代码 */
let entity
const toBegin = () => {
  // 获取到总时长和各站点时间
  timeObj = getSiteTimes(positions, 20)
  const start = Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16))
  const stop = Cesium.JulianDate.addSeconds(start, timeObj.timeSum, new Cesium.JulianDate())
  //Make sure viewer is at the desired time.
  viewer.clock.startTime = start.clone()
  viewer.clock.stopTime = stop.clone()
  viewer.clock.currentTime = start.clone() //当前时间
  // const proportion = getProportion()
  // viewer.clock.currentTime = Cesium.JulianDate.addSeconds(
  //   start,
  //   timeObj.timeSum * proportion,
  //   new Cesium.JulianDate()
  // ) //当前时间
  // 添加每个轨迹点的坐标和对应的系统时间——核心代码
  const position = getSampleData(positions, start, timeObj.siteTime)
  // 添加可运动的实体
  entity = viewer.entities.add({
    availability: new Cesium.TimeIntervalCollection([
      new Cesium.TimeInterval({
        start: start,
        stop: stop
      })
    ]),
    model: {
      uri: '/src/assets/bus.gltf',
      minimumPixelSize: 40,
      scale: 1.2
    },
    position: position,
    // 自动修正方向
    orientation: new Cesium.VelocityOrientationProperty(position)
  })
  // 视角跟踪
  viewer.trackedEntity = entity
  data.carInfo.peopleNum = Math.ceil(Math.random() * (30 - 10)) + 10
  img.value.style.top = '0.3em'
  img.value.style.left = '0.2em'
  data.control.percentage = 0
  // data.index = 0;
  bubble = new Bubble({
    viewer: viewer,
    carInfo: data.carInfo
  })
  // 时间流逝的监听
  viewer.clock.onTick.addEventListener(tickEventHandler)
}

/* 公交车模型向任意点位跳转 */
const getProportion = () => {
  // 计算任意点到起点距离占全长的百分比
  const start = turf.point(lineGeoJson.geometry.coordinates[0])
  const stop = turf.point([114.406979, 30.462812])
  const sliced = turf.lineSlice(start, stop, lineGeoJson)
  // 拿到最近的下一个站点的index
  loadData.stations.forEach((item, i) => {
    const stationStop = item.xy_coords.split(';').map((a) => Number(a))
    const stationSliced = turf.lineSlice(start, stationStop, lineGeoJson)
    if (turf.length(stationSliced) < turf.length(sliced)) {
      console.log(i)
      data.index = i
    }
  })
  return turf.length(sliced) / turf.length(lineGeoJson)
}
/*时间流逝监听方法中的回调函数*/
let isInCar = false
const tickEventHandler = () => {
  // 获取当前时间的的坐标，并转换为经纬度后添加高度再转换为笛卡尔坐标
  let startPosition = entity.position.getValue(viewer.clock.currentTime)
  let cartographic = Cesium.Cartographic.fromCartesian(startPosition)
  let lon = Cesium.Math.toDegrees(cartographic.longitude)
  let lat = Cesium.Math.toDegrees(cartographic.latitude)
  let newPosition = Cesium.Cartesian3.fromDegrees(lon, lat, 2)
  bubble.changePosition(newPosition) // 实时改变弹窗位置
  // 进度条
  /// 计算当前时间与开始时间的时间差
  /// Cesium 里面的时间格式不是简单的数字格式，求时间差不能之间减法，需要用特定的方法
  const diffTime = Cesium.JulianDate.secondsDifference(
    viewer.clock.currentTime,
    Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16))
  )
  data.control.percentage = (diffTime / timeObj.timeSum).toFixed(5) * 100
  /// 进度条== diffTime/844

  // 站点停车
  /// 获取实时坐标，并与下一站点的距离进行比较，以达到在站点停车的功能，b并更新弹窗数据，最后一个站点结束动画
  let a = entity.position.getValue(viewer.clock.currentTime) // getValue方法获取当前时间的坐标
  const b = Cesium.Cartesian3.fromDegrees(
    ...loadData.stations[data.index + 1].xy_coords.split(';').map((a) => Number(a))
  )
  const distance = spaceDistance(a, b)
  if (distance < 10) {
    data.index++
    if (data.index > 13) {
      img.value.style.top = '8em'
      img.value.style.left = 0.2 + (data.index - 14) * 1.67 + 'em'
    } else {
      img.value.style.left = 0.2 + data.index * 1.67 + 'em'
    }
    if (data.index == loadData.stations.length - 1) {
      removeEvent()
      return
    }
    data.fromName = loadData.stations[data.index].name
    data.toName = loadData.stations[data.index + 1].name
    data.carInfo.peopleNum = Math.ceil(Math.random() * (30 - 10)) + 10
    viewer.clock.shouldAnimate = false
    setTimeout(() => {
      viewer.clock.shouldAnimate = true
    }, 1500)
  }

  // 车内视角的实现
  if (isInCar) {
    viewer.trackedEntity = null
    let orientation = entity.orientation.getValue(viewer.clock.currentTime)
    let transform = Cesium.Matrix4.fromRotationTranslation(
      Cesium.Matrix3.fromQuaternion(orientation),
      newPosition
    )
    viewer.camera.lookAtTransform(transform, new Cesium.Cartesian3(-0.0001, 0.0, 0.0))
  }
}

/* 视角切换 */
const changeView = (type) => {
  if (type == 'incar') {
    isInCar = true
  } else if (type == 'follow') {
    isInCar = false
    viewer.trackedEntity = entity
  } else if (type == 'free') {
    isInCar = false
    viewer.trackedEntity = null
    viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
  }
}
/* 站点跳转*/
const toStation = (index) => {
  changeView('free')
  if (pickLabel) {
    pickLabel.label.fillColor = Cesium.Color.WHITE
  }
  pickLabel = viewer.entities.getById('label' + index)
  pickLabel.label.fillColor = Cesium.Color.YELLOW
  viewer.flyTo(pickLabel)
}

/* 清除动画：关闭弹窗、关闭监听、移除汽车、解除视角、 暂停时间*/
const removeEvent = () => {
  bubble.windowClose()
  viewer.clock.onTick.removeEventListener(tickEventHandler)
  viewer.entities.remove(entity)
  changeView('free')
  isBegin = false
  data.control.play = false
  viewer.clock.shouldAnimate = false
}

onMounted(() => {
  Cesium.Ion.defaultAccessToken =
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5ZGJlOTBmMi0xYjNmLTRhMzYtOTlhMy0zNGIwOGUyNGFkYTUiLCJpZCI6MTE1MTg3LCJpYXQiOjE2Njg2NzM2NTB9.kQwPYh1h1KTzNcwTFkoW36f8DpbDGhdDhrpzHHFUfTk'

  const layer = new Cesium.UrlTemplateImageryProvider({
    url: 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
    minimumLevel: 4,
    maximumLevel: 18
  })
  // viewer 是所有API的开始
  viewer = new Cesium.Viewer('cesiumContainer', {
    imageryProvider: layer,
    baseLayerPicker: false, //是否显示图层选择控件
    animation: false, //是否显示动画控件
    timeline: false, //是否显示时间轴控件
    fullscreenButton: false, //是否显示全屏按钮
    geocoder: false, //是否显示搜索按钮
    homeButton: false, //是否显示主页按钮
    navigationHelpButton: false, //是否显示帮助提示按钮
    sceneModePicker: false, //是否显示投影方式按钮
    shouldAnimate: true,
    infoBox: false,
    selectionIndicator: false
  })
  modifyMap(viewer, {
    // 反色？
    invertColor: true,
    // 滤色值
    filterRGB: [70, 110, 120]
  })

  // 公交路线、标签、站牌的初始化
  initData()
})
/* 页面切换时关闭监听器*/
document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    viewer.clock.shouldAnimate = false
    data.control.play = false
  }
})
</script>
<style lang="scss">
* {
  margin: 0;
  padding: 0;
}
:root {
  box-sizing: border-box;
  font-size: calc(0.5vw + 0.65em);
}
#cesiumContainer {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}
.container {
  display: flex;
  flex-direction: column;
  background-color: red;
  position: absolute;
  right: 1em;
  top: 6em;
  z-index: 999;
  height: auto;
  width: auto;
  .fromTo {
    display: flex;
    width: 24em;
    height: 3.5em;
    z-index: 999;
    background-color: rgb(12, 105, 148);
    padding:
      0.5em 0.75em 0,
      5em;
    border: 1px solid #4f7cb1;
    justify-content: space-between;
    div {
      color: #fff;
      font-size: 1.25em;
      line-height: 3.5em;
    }
  }
  .carInfo {
    position: relative;
    z-index: 999;
    background-color: #13396a;
    padding:
      0.5em 0.75em 0,
      5em;
    border: 1px solid #4f7cb1;
    width: 24em;
    height: 17.8em;
    overflow: hidden;
    .itemStation {
      color: #fff;
      font-size: 0.7rem;
      float: left;
      width: 1.78em;
      height: 8.79em;
      text-align: center;
      font-family: 'Microsoft YaHei', 微软雅黑;
      line-height: 1em;
      cursor: pointer;
      word-break: normal;
      overflow: hidden;
      margin: 1.78em 0.3em 0.7em 0.3em;
    }
    .img {
      width: 1.2em;
      position: absolute;
      transition: all 0.7s ease 0s;
      left: 0.2em;
      cursor: pointer;
      z-index: 1;
      backface-visibility: hidden;
      display: block;
    }
    .num {
      display: inline-block;
      width: 1em;
      font-family: arial;
      padding-bottom: 0.3em;
      cursor: pointer;
    }
  }
  .control {
    padding:
      0.5em 0.75em 0,
      5em;
    border: 1px solid #4f7cb1;
    width: 24em;
    height: 4.5em;
    z-index: 999;
    background-color: rgb(12, 105, 148);
    border: 1px solid #4f7cb1;
    .iconList {
      color: #fff;
      font-size: 1.5rem;
      margin-top: 0.17em;
      margin-left: 0.17em;
      margin-right: 0.17em;
      .el-icon {
        margin-right: 0.2em;
        cursor: pointer;
      }
      .el-icon:hover {
        color: #7c4dff !important;
      }
      .speed {
        float: right;
        margin-top: 0.1em;
        font-size: 0.8em;
      }
      .el-button.el-button--primary {
        font-size: 0.75rem;
        width: 4.7em;
        height: 1.8em;
        padding: 0 0.17em;
        margin-top: -0.4em;
      }
    }
  }
}
.cesium-viewer-bottom {
  display: none;
}
</style>
